/*
 *  $Id: LocalizedPhrase.cs 1229 2010-12-05 14:41:22Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008-2010 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;
using System.Text;

using Ragima.Languages.PhraseParts;

namespace Ragima.Languages {
    /// <summary>
    /// Абстрактный класс для фразы (шаблонной строки текста) на отдельном языке.
    /// </summary>
    public abstract class LocalizedPhrase : ILocalized {
        /// <summary>
        /// Статический экземпляр пустой фразы.
        /// </summary>
        public static readonly LocalizedPhrase Empty = new EmptyPhrase();

        /// <summary>
        /// Список стандартных парсеров фраз.
        /// </summary>
        private static List<IPhrasePartParser> Parsers = null;

        private List<PhrasePart> _parts;

        public LocalizedPhrase() {
            _parts = new List<PhrasePart>();
        }
        public LocalizedPhrase(IEnumerable<PhrasePart> parts) {
            _parts = new List<PhrasePart>(parts);
        }
        public LocalizedPhrase(string template) : this() {
            Parse(template);
        }

        /// <summary>
        /// Получает язык по типу.
        /// </summary>
        public virtual Language Language {
            get { return Language.AllLanguages.GetLanguage<LocalizedPhrase>(this); }
        }

        /// <summary>
        /// Получает список парсеров фраз.
        /// </summary>
        protected virtual IEnumerable<IPhrasePartParser> GetParsers() {
            if (Parsers == null) {
                Parsers = new List<IPhrasePartParser>();
                Parsers.Add(new IntPhrasePart.IntParser());
                Parsers.Add(new ArgPhrasePart.ArgParser());
            }
            return Parsers;
        }

        /// <summary>
        /// Получает текст фразы с указанными аргументами.
        /// </summary>
        public virtual string GetText(params object[] args) {
            if (_parts.Count == 0)
                return string.Empty;
            if (_parts.Count == 1 && _parts[0] is TextPhrasePart)
                return _parts[0].ToString();
            var builder = new PhraseBuilder(Language);
            foreach (var part in _parts)
                part.Build(builder, args);
            return builder.ToString();
        }

        /// <summary>
        /// Выдает фразу в виде строки (получая её без аргументов).
        /// </summary>
        public override string ToString() {
            return GetText();
        }

        /// <summary>
        /// Получает текст фразы (с подсказками) с указанными аргументами.
        /// </summary>
        public virtual IEnumerable<HintedPhraseText> GetHintedText(params object[] args) {
            if (_parts.Count == 0)
                yield break;
            if (_parts.Count == 1 && _parts[0] is TextPhrasePart) {
                yield return new HintedPhraseText(_parts[0].ToString(), null);
                yield break;
            }
            var builder = new PhraseBuilder(Language);
            foreach (var part in _parts) {
                var hint = part.GetHintObject(args);
                if (hint != null && builder.Text.Length > 0)
                    yield return builder.GetHintedText(null);
                part.Build(builder, args);
                if (hint != null && builder.Text.Length > 0)
                    yield return builder.GetHintedText(hint);
            }
            if (builder.Text.Length > 0)
                yield return builder.GetHintedText(null);
        }

        /// <summary>
        /// Получает исходный шаблон фразы из её разобранных частей.
        /// </summary>
        public string GetTemplate() {
            var builder = new PhraseBuilder(Language);
            foreach (var part in _parts)
                part.BuildTemplate(builder);
            return builder.ToString();
        }

        /// <summary>
        /// Разбирает указанный шаблон на части фразы для ускорения дальнейшей работы.
        /// </summary>
        private void Parse(string template) {
            var scanner = new PhraseTemplateScanner(template);
            while (!scanner.IsEnd) {
                if (scanner.IsCommand) {
                    scanner.NextChar();
                    var part = ParsePart(scanner);
                    if (part != null)
                        _parts.Add(part);
                    scanner.SkipCommand();
                } else {
                    var text = scanner.GetText();
                    if (!string.IsNullOrEmpty(text))
                        _parts.Add(new TextPhrasePart(text));
                }
            }
        }

        /// <summary>
        /// Разбирает часть фразы, являющуюся командой.
        /// </summary>
        private PhrasePart ParsePart(PhraseTemplateScanner scanner) {
            var hasArg = scanner.IsNumber;
            var argNum = hasArg ? scanner.GetNumber() : 0;
            foreach (var parser in GetParsers())
                if (parser.NeedArgument == hasArg && TestParser(scanner, parser))
                    return parser.Create(argNum, scanner);
            return null;
        }

        /// <summary>
        /// Проверяет парсер с сохранением текущей позиции в сканере.
        /// </summary>
        private bool TestParser(PhraseTemplateScanner scanner, IPhrasePartParser parser) {
            var pos = scanner.Position;
            var test = parser.Test(scanner);
            scanner.Position = pos;
            return test;
        }

        /// <summary>
        /// Фраза, всегда возвращающая пустую строку.
        /// </summary>
        private class EmptyPhrase : LocalizedPhrase {
            public override Language Language {
                get { return Language.CurrentLanguage; }
            }

            public override string GetText(params object[] args) {
                return string.Empty;
            }
        }
    }
}
